﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;

namespace FCMS.Data.Linq
{
    internal class UpdateColumnBuilder : ExpressionVisitor
    {
        private List<object> m_arguments;
        private Stack<string> m_statementParts;

        public object[] Arguments { get; private set; }
        public string Statement { get; private set; }

        public void Build(Expression expression)
        {
            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }

            MemberInitExpression memberInitExp = expression as MemberInitExpression;
            if (memberInitExp == null)
            {
                throw new ArgumentException("Must be MemberInitExpression.", "expression");
            }

            PartialEvaluator evaluator = new PartialEvaluator();
            List<string> parts = new List<string>(memberInitExp.Bindings.Count);

            this.m_arguments = new List<object>();
            this.m_statementParts = new Stack<string>();

            foreach (MemberAssignment assignment in memberInitExp.Bindings)
            {
                this.m_statementParts.Clear();

                var evaluatedExp = evaluator.Eval(assignment.Expression);
                this.Visit(evaluatedExp);

                string s = this.m_statementParts.Count > 0 ? this.m_statementParts.Pop() : null;
                string statement = assignment.Member.Name + " = " + s;
                parts.Add(statement);
            }

            this.Arguments = this.m_arguments.ToArray();
            this.Statement = String.Join(", ", parts.ToArray());
        }

        protected override Expression VisitBinary(BinaryExpression b)
        {
            if (b == null) return b;

            string opr;
            switch (b.NodeType)
            {
                case ExpressionType.Add:
                    opr = "+";
                    break;
                case ExpressionType.Subtract:
                    opr = "-";
                    break;
                case ExpressionType.Multiply:
                    opr = "*";
                    break;
                case ExpressionType.Divide:
                    opr = "/";
                    break;
                default:
                    throw new NotSupportedException(b.NodeType + "is not supported.");
            }

            this.Visit(b.Left);
            this.Visit(b.Right);

            string right = this.m_statementParts.Pop();
            string left = this.m_statementParts.Pop();

            string condition = String.Format("({0} {1} {2})", left, opr, right);
            this.m_statementParts.Push(condition);

            return b;
        }

        protected override Expression VisitConstant(ConstantExpression c)
        {
            if (c == null) return c;

            this.m_arguments.Add(c.Value);
            this.m_statementParts.Push(String.Format("{{{0}}}", this.m_arguments.Count - 1));

            return c;
        }

        protected override Expression VisitMemberAccess(MemberExpression m)
        {
            if (m == null) return m;

            PropertyInfo propertyInfo = m.Member as PropertyInfo;
            if (propertyInfo == null) return m;

            this.m_statementParts.Push(String.Format("[{0}]", propertyInfo.Name));

            return m;
        }
    }
}
